package middleware

import (
	"auth/authentication"
	"auth/bll"
	log "auth/collector/logger"
	"auth/model"
	"auth/model/entity"
	"auth/store/postgres"
	"auth/store/redis"
	"context"
	"github.com/gin-gonic/gin"
	"net/http"
	"strings"
	"time"
)

type void struct{}

var v void
var whitelist = map[string]void{
	"/v1/sms":                       v,
	"/v1/sms/send":                  v,
	"/v1/sms/check":                 v,
	"/v1/login":                     v,
	"/auth/v1/login":                v,
	"/v1/app/login":                 v,
	"/auth/v1/app/login":            v,
	"/v1/app/promotionalImage/list": v,
	"/v1/app/vehicleSkill/list":     v,
	"/v1/app/forgot_password":       v,
	"/v1/app/phone_bind/apple":      v,
	"/v1/app/phone_bind/wx":         v,
	"/v1/app/vehicleSkill/detail":   v,
	"/v1/devices/erp/callback":      v,
}

func Auth() gin.HandlerFunc {
	var (
		err     error
		options *authentication.Options
		user    *model.UserInfo
	)

	return func(c *gin.Context) {
		//白名单
		fu := c.GetHeader("X-Forwarded-Uri")

		if len(fu) >= 0 {
			if _, ok := whitelist[fu]; ok {
				log.Info("whitelist passed" + fu)
				c.JSON(http.StatusOK, "ok")
				c.Abort()
				return
			}
		}

		authorization := c.GetHeader("Authorization")
		log.Info("oath req:url:" + fu + " Authorization:" + authorization)
		// 拆分token
		parts := strings.SplitN(authorization, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			c.JSON(http.StatusUnauthorized, nil)
			c.Abort()
			return
		}

		if options, err = authentication.Extract(parts[1]); err != nil {
			c.JSON(http.StatusUnauthorized, nil)
			c.Abort()
			return
		}
		userAgent := c.Request.UserAgent()
		log.Info("----------------------------")
		log.Info("客户端UserAgent值:", userAgent)
		log.Info("----------------------------")
		// 如果是非web客户端
		if appOrWeb(userAgent) != 0 {
			// 判断缓存 在缓存中不存在就代表失效
			token, err := redis.UserRedis.GetLoginToken(c.Request.Context(), options.UserID)
			// 如果token不正确，返回异常
			if err != nil || token != parts[1] {
				c.JSON(http.StatusUnauthorized, nil)
				c.Abort()
				return
			}
			go func(uid int64) {
				bg := context.Background()
				redis.UserRedis.LoginActive(bg, uid)
				now := time.Now()
				yesterday := now.AddDate(0, 0, -1)
				count, err := postgres.UserActive.Exist(yesterday)
				defer func() {
					if r := recover(); r != nil {
						log.Info("异常捕获")
					}
				}()
				// 全局锁，防止并发插入多个条记录 // 最好是一个mq异步任务
				if redis.UserRedis.TryLock(bg, "lock") {
					if !count {
						timeStr := yesterday.Format("2006-01-02")
						c, _ := redis.UserRedis.GetActiveUserCount(bg, timeStr)
						active := &entity.UserActive{
							Year:      int64(yesterday.Year()),
							Month:     int64(yesterday.Month()),
							Day:       int64(yesterday.Day()),
							Total:     c,
							CreatedAt: now.Unix(),
							UpdatedAt: now.Unix(),
						}
						_, err = postgres.UserActive.Create(active)
						log.Info(err)
					}
				}
			}(options.UserID)
		}
		user, err = bll.User.FindEntityById(options.UserID)

		if err != nil {
			c.Error(err)
			c.Abort()
			return
		}
		ctx := c.Request.Context()
		ctx = context.WithValue(ctx, authentication.AuthUserKey, user.ID)
		ctx = context.WithValue(ctx, authentication.TokenDataKey{}, options)
		c.Request = c.Request.WithContext(ctx)
		c.Next()
	}
}

// 0 为web段，其他都是app端
func appOrWeb(userAgent string) int {
	agent := strings.ToLower(userAgent)
	if strings.Contains(agent, "android") || strings.Contains(agent, "okhttp") {
		log.Info("安卓客户端")
		return 1
	} else if strings.Contains(agent, "iphone") {
		log.Info("iphone客户端")
		return 2
	} else if strings.Contains(agent, "ipad") {
		log.Info("IPad客户端")
		return 3
	} else {
		log.Info("其他客户端")
		return 0
	}
}
